package sagroup.dracots.testing;

import java.util.HashSet;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;

import lights.adapters.Field;
import lights.adapters.Tuple;
import lights.interfaces.ITuple;
import lime2.Location;
import lime2.TupleID;

import org.apache.commons.math.random.RandomData;
import org.apache.commons.math.random.RandomDataImpl;

import sagroup.dracots.DracoConstants;
import sagroup.dracots.DracoTS;
import sagroup.dracots.FlatShareTuple;
import sagroup.dracots.TupleType;

public class OutAgentPeriod implements Runnable {

	DracoTS dts = null;

	// This is the average value for exponential distribution of out frequency
	private long meaninternal;
	// This is the params for Gaussian distribution for size of tuple
	private long mu, sigma;
	// Type of tuple
	private TupleType type;

	Timer timer = new Timer();

	private boolean timesup = false;
	private int opCount = 0;
	private long begin;
	private long timing;

	private boolean timesupPeriod = false;

	private Logger LOG = Logger.getLogger(this.getClass().getName());

	// Make a new single tuple element
	static FlatShareTuple makeNewTuple(TupleType type, String aString) {
		ITuple it = new Tuple();
		it.add(new Field().setValue("switch"));
		it.add(new Field().setValue(aString));
		return new FlatShareTuple(type, it);
	}

	private FlatShareTuple createLocalMasterTypeTemplate(TupleType type) {
		FlatShareTuple template = (FlatShareTuple) FlatShareTuple
				.createMatchAllTemplateByClass(FlatShareTuple.class);

		template.setType(type);

		template.setCurr(Location.HERE);
		template.setDest(Location.HERE);
		template.setReplica(false);

		return template;

	}

	/**
	 * 
	 */
	public OutAgentPeriod(String typeName, String meaninternal, String mu,
			String sigma) {

		this.type = new TupleType(typeName);

		this.meaninternal = Long.valueOf(meaninternal);
		this.mu = Long.valueOf(mu);
		this.sigma = Long.valueOf(sigma);

		if (this.meaninternal < 0 || this.mu < 0 || this.sigma < 0) {
			throw new IllegalArgumentException("Parameters are illegal.");
		}

		TimerTask tt = new TimerTask() {
			@Override
			public void run() {
				timesup = true;
				timing = (System.nanoTime() - begin) / 1000 / 1000;
			}
		};

		TimerTask ttPeriod = new TimerTask() {
			@Override
			public void run() {
				timesupPeriod = true;
				dts.ing(createLocalMasterTypeTemplate(type));
			}
		};
		timer.schedule(tt, AgentUtil.expTime);
		timer.schedule(ttPeriod, DracoConstants.ADAPTATION_PERIOD);

	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			dts = DracoTS.get("testing");
			RandomData randomTimeGap = new RandomDataImpl();
			RandomData randomSize = new RandomDataImpl();

			System.out.println("type: " + type + " Mean interval: "
					+ meaninternal);

			HashSet<TupleID> hashSet = new HashSet();
			while (!this.timesup) {
				while (!this.timesupPeriod) {
					double tupleSize = randomSize.nextGaussian(mu, sigma);
					char[] pChar = new char[(int) tupleSize];
					if (pChar == null) {
						System.out.println("Allocate failed!");
						System.exit(0);
					} else {
						pChar[0] = 'a';
						pChar[(int) tupleSize - 1] = 'z';
					}

					String aString = new String(pChar);
					FlatShareTuple tempTuple = makeNewTuple(type, aString);
					hashSet.add(tempTuple.getVID());
					dts.out(tempTuple);
					// System.out.println(Thread.currentThread().getName()
					// + " has outed " + tempTuple.getVID());
					// System.out.println("Number: " + i + "\tLength: " +
					// aString.length());

					opCount++;
					long sleeptime = (long) (randomTimeGap.nextExponential(1) * this.meaninternal);
					Thread.sleep(sleeptime);
				}
				System.out.println(Thread.currentThread().getName()
						+ " has outed " + opCount + " with type " + type);
				timesupPeriod = false;
				opCount = 0;
				TimerTask ttPeriod = new TimerTask() {
					@Override
					public void run() {
						timesupPeriod = true;
						dts.ing(createLocalMasterTypeTemplate(type));
					}
				};
				timer.schedule(ttPeriod, DracoConstants.ADAPTATION_PERIOD);
			}
			dts.out(ListTuple.createLocalList(type, hashSet));
			System.out.println(this.toString() + " Agent finishs all work.");

		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			LOG
					.info("Out Agent ��Ϣ : " + this.toString() + " "
							+ this.getInfo());
		}
	}

	public String toString() {
		String id = "Agent Out " + ":" + Thread.currentThread().getName();
		String target = "Target type : " + type + " ; Mean interval: "
				+ this.meaninternal + " .";
		return id + ", " + target;
	}

	private String getInfo() {
		String info = "Operation times : " + this.opCount + " Timing : "
				+ this.timing + ". Average : "
				+ ((this.opCount != 0) ? this.timing / this.opCount : null);
		return info;
	}
}
